﻿using System;
using System.Collections.Generic;
using System.Linq;
using Win32.WtsApi32;
using System.Windows.Forms;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing;
using RDPAddins.Common;

namespace RDPAddins
{
    class RDPClient 
    {
        IntPtr initHandle = IntPtr.Zero;
        ChannelInitEventDelegate channelInitEventDelegate;

        [ImportMany(typeof(IAddin))]
        IEnumerable<Lazy<IAddin, IAddinMetadata>> Addins { get; set; }

        List<Channel> Channels;
        public ChannelEntryPoints EntryPoint { get; private set; }
        
        internal SlaveMode MainForm { get; set; }
        
        RDPClient Client;

        public ChannelReturnCodes Initialize(string Path, IntPtr entry)
        {
            Client = this;
            var pluginspath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Path), "Plugins");
            try
            {
                if (Directory.Exists(pluginspath))
                {
                    EntryPoint = (ChannelEntryPoints)Marshal.PtrToStructure(entry, typeof(ChannelEntryPoints));
                    channelInitEventDelegate = new ChannelInitEventDelegate(VirtualChannelInitEventProc);
                    var batch = new CompositionBatch();
                    batch.AddPart(this);
                    var catalog = new DirectoryCatalog(pluginspath);
                    var container = new CompositionContainer(catalog);
                    container.Compose(batch);
                    Channels = Addins.Select(addin =>
                    {
                        return new Channel(this, addin);
                    }).ToList();
                    Channels.ForEach(channel =>
                    {
                        MainForm.Invoke(new Action(() =>
                        {
                            channel.CreateUI();
                        }));
                    });
                    var cds = Channels.Select(channel => channel.ChannelDef).ToArray();
                    return EntryPoint.VirtualChannelInit(ref initHandle, cds, cds.Length, 1, channelInitEventDelegate);
                }
            }
            catch (Exception ex) { MessageBox.Show(ex.Message, "RDPAddins", MessageBoxButtons.OK, MessageBoxIcon.Error); }
            return ChannelReturnCodes.NullData;
        }

        void ChannelsInitialized()
        {
            MainForm.Invoke(new Action(() =>
            {
                if (MainForm.Opacity == 0)
                {
                    MainForm.Hide();
                    MainForm.Opacity = 100;
                }
            }));
        }

        void VirtualChannelInitEventProc(IntPtr initHandle, ChannelEvents initEvent, byte[] data, uint dataLength)
        {
            switch (initEvent)
            {
                case ChannelEvents.Initialized:
                    ChannelsInitialized();
                    Client.Channels.ForEach(channel => channel.InitializedInternal());
                    break;
                case ChannelEvents.Connected:
                    string server = System.Text.Encoding.Unicode.GetString(data);
                    server = server.Substring(0, server.IndexOf('\0'));
                    Client.Channels.ForEach(channel =>
                    {
                        ChannelReturnCodes ret = channel.ConnectedInternal(initHandle);
                        if (ret != ChannelReturnCodes.Ok)
                        {
                            MessageBox.Show(string.Format("{0}: VirtualChannelOpen error: {1}", channel.Metadata.ChannelName, ret), "RDPAddins", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                    });
                    MainForm.Invoke(new Action(() =>
                        {
                            MainForm.Text = "RDPAddnins: " + server;
                        }));

                    break;
                case ChannelEvents.V1Connected:
                    MessageBox.Show("Bad RDP server version.", "RDPAddins", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
                case ChannelEvents.Disconnected:
                    MainForm.Invoke(new Action(() => MainForm.Text = "RDPAddnins"));
                    Channels.ForEach(channel => channel.DisconnectedInternal());
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    break;
                case ChannelEvents.Terminated:
                    Channels.ForEach(channel => channel.TerminatedInternal());
                    MainForm.RealClosing = true;
                    MainForm.Invoke(new Action(() => MainForm.Close()));
                    break;
            }
        }

        
    }
}